﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Xml.Serialization;

using Idlesoft.Utils;

namespace Idlesoft.Cli
{
	/// <summary>
	/// Inherits from XmlElementAttribute used for xml serialization; implements ICliVirtue used in CliBuilder operations.
	/// </summary>
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
	public class CliElementAttribute : XmlElementAttribute, ICliVirtue
	{
		#region ctors

		/// <summary>
		/// Ctor that allows the user to specify the flags to be used rather than be auto-generated.
		/// </summary>
		/// <param name="flags">Specific flags to be used.</param>
		public CliElementAttribute(params string[] flags)
		{
			Flags = flags;
			FlagTypes = FlagTypes.Undef;
		}
		#endregion ctors

		#region props

		/// <summary>
		/// Boolean property to determine if the virtue is an Option as opposed to a BuiltIn or Required.
		/// </summary>
		public bool Option { get { return !BuiltIn && !Required; } }

		/// <summary>
		/// Boolean property to determin if the virtue is decorating the CliBuilder class instead of a user-defined one.
		/// </summary>
		public bool BuiltIn { get { return this.MemberInfo.DeclaringType.Equals(typeof(CliBuilder)); } }

		/// <summary>
		/// Optional boolean property to mark a virtue as required.  This will cause an error to happen and the usage to print if it is not set during CliBuilder.Create.
		/// </summary>
		public bool Required { get; set; }

		/// <summary>
		/// Optional property to override the default flag types as defined during the call to CliBuilder.Create or its default value.  This governs how and how many of the flags are auto-generated.
		/// </summary>
		public FlagTypes FlagTypes { get; set; }

		/// <summary>
		/// Optional property to override the name of the property.  This overridden Name is then used when auto-generating the flags to match against.
		/// </summary>
		public string Name { get; set; }

		/// <summary>
		/// Optional property used when the help usage prints to the console on bad input are when invoked.
		/// </summary>
		public string Description { get; set; }

		/// <summary>
		/// Optional string array of flags to be used to match against.  This turns off the auto-generated flags irrespectives of the FlagTypes etc.
		/// </summary>
		public string[] Flags { get; set; }

		/// <summary>
		/// Internal property used to store the values once they have been parsed out of the command line args.  Do not attempt to set this property.
		/// </summary>
		public object[] Values { get; set; }

		/// <summary>
		/// Optional property to supply default values to be used when no values are found during parsing.  Note that this will not be used for virtues marked as Required.
		/// </summary>
		public object Default { get; set; }

		/// <summary>
		/// Internal property to store the MemberInfo loaded during the CliBuilder.Create execution.  Do not attempt to set this property.
		/// </summary>
		public MemberInfo MemberInfo { get; set; }

		/// <summary>
		/// Internal property to get the type of the property|field that the MemberInfo property points to.  Will throw an exception if MemberInfo is not set.
		/// </summary>
		public Type MemberInfoType
		{
			get
			{
				if (MemberInfo is PropertyInfo)
					return (MemberInfo as PropertyInfo).PropertyType;
				else if (MemberInfo is FieldInfo)
					return (MemberInfo as FieldInfo).FieldType;
				return null;
			}
		}
		#endregion props

		#region methods

		/// <summary>
		/// Internal method to determin if a virtue has been assigned.  Checks against the default values; this will probably be improved to be more accurate.
		/// </summary>
		/// <param name="target">The target object that the MethodInfo property is point to.</param>
		/// <returns></returns>
		public bool IsAssigned(object target)
		{
			bool isAssigned = false;
			if (target != null)
			{
				if (MemberInfo is PropertyInfo)
					isAssigned = (MemberInfo as PropertyInfo).GetValue(target, null) != this.MemberInfoType.Default();
				else if (MemberInfo is FieldInfo)
					isAssigned = (MemberInfo as FieldInfo).GetValue(target) != this.MemberInfoType.Default();
			}
			return isAssigned;
		}
		public override string ToString()
		{
			return string.Format("Name={0} Types={1} Flags={2} Values={3}", Name, FlagTypes, Flags.Flatten("|"), Values.Flatten("|"));
		}
		public override bool Equals(object obj)
		{
			bool equals = false;
			CliAttributeAttribute rhs = obj as CliAttributeAttribute;
			if (rhs != null)
			{
				if (MemberInfo != null && rhs.MemberInfo != null)
					equals = MemberInfo.Equals(rhs.MemberInfo);
			}
			return equals;
		}
		public override int GetHashCode()
		{
			return base.GetHashCode();
		}
		#endregion methods
	}
}
